/*
 * PopupMessageWidget.cpp
 *
 *  Created on: Jul 11, 2012
 *      Author: guillou
 */

#include "PopupSettings.hpp"
#include "PopupUsersManager.hpp"
#include "PopupThreadWidget.hpp"
#include "PopupUtilities.hpp"
#include <PopupMessageWidget.hpp>
#include <PopupThreadStartWidget.hpp>
#include <PopupCanvassWidget.hpp>

using namespace std;
using namespace utilities;
using namespace Popup;
using namespace PopupGui;

PopupMessageWidget::PopupMessageWidget(QWidget *parent)
: QTabWidget(parent), m_parent(parent),
  m_mutex(QMutex::Recursive), threadCounter(1),
  anonymousThreadCounter(1),
  m_threadStartWidget(new PopupThreadStartWidget(parent)),
  m_messageBox(QMessageBox::NoIcon,
               QString::fromUtf8("T'as des bollocks?"), "",
               QMessageBox::Ok|QMessageBox::Cancel)
{
  setIconSize(QSize(20, 20));

  // Insert thread starter widget
  QIcon _addThreadIcon;
  _addThreadIcon.addFile(QString::fromUtf8(":/images/popup_plus_blue.png"),
                         QSize(32,32));
  insertTab(0, m_threadStartWidget, _addThreadIcon, "");

  // Set shortcuts
  QAction *_startAction = new QAction(tr("&Start"), m_threadStartWidget);
  QList<QKeySequence> _startShortcuts;
  _startShortcuts << QKeySequence(Qt::CTRL + Qt::Key_Return);
  _startShortcuts << QKeySequence(Qt::CTRL + Qt::Key_Enter);
  _startAction->setShortcuts(_startShortcuts);
  _startAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
  m_threadStartWidget->addAction(_startAction);
  connect(_startAction, SIGNAL(triggered()), this, SLOT(slot_onStart()));

  connect(m_threadStartWidget->startButton(), SIGNAL(clicked()),
          this, SLOT(slot_onStart()));
  connect(this, SIGNAL(currentChanged(int)),
          this, SLOT(slot_currentChanged(int)));
}

//==============================================================================
// Message processing functions
//==============================================================================

void PopupMessageWidget::processNewMessage(const UserMessage & p_message)
{
  PopupThreadWidget *_twidget = 0;

  m_mutex.lock(); // This will lock only if user is deleting a tab

  // Search for an existing thread matching this message's one
  map<ThreadID, PopupThreadWidget*>::iterator _it =
      m_threads.find(p_message.threadID);

  // Found! Let's point on it
  if (_it != m_threads.end())
  {
    _twidget = _it->second;
  }
  // Not Found, let's create a new one
  else
  {
    UserList _attendants;
    _attendants.insert(p_message.targets.begin(), p_message.targets.end());
    _attendants.insert(p_message.senderID);
    _twidget = createNewThread(p_message.threadID,
                               p_message.title.c_str(),
                               _attendants);
  }

  // Append the new message
  _twidget->slot_showMessage(&p_message);

  // In case widget thread title is not valid,
  if (_twidget->doThreadTitleUpdate(p_message.title.c_str()))
  {
    setTabText(indexOf(_twidget), p_message.title.c_str());
  }

  // We are ready to display!
  slot_showMessage(&p_message, _twidget);

  m_mutex.unlock();
}
//----------------------------------------------------------

bool PopupMessageWidget::displayAuthorized(ReceivedMsgType p_type)
{
  PopupUserExt _me;
  getUserInfo(_me);
  bool _show = true;
  bool _doPrompt = true;
  static bool _promptInProgress = false;

  // No way in case user has muted the application!
  if (_me.getMode() == POPUP_MODE_MUTED)
  {
    _show = false;
  }
  else
  {
    _show = PopupSettings::instance()->isDisplayAllowed(p_type);

    if (_show && (_me.getMode() == POPUP_MODE_PROMPT))
    {
      m_promptMutex.lock();
      if (_promptInProgress == true) {
        _doPrompt = false;
        _show = false;
      } else if (!m_parent->isVisible()) {
        _doPrompt = true;
        _promptInProgress = true;
      } else {
        _doPrompt = false;
        _promptInProgress = false;
      }
      m_promptMutex.unlock();

      if (_doPrompt)
      {
        _show = false;
        if (PopupSettings::instance()->useFakePrompt() == true) {
          _show = (QMessageBox::critical(this, tr("Application error"),
                                         QString("The instruction at 0xa1ef1571 "
                                             "referenced memory at 0x6eb15abe.\n"
                                             "The memory could not be read."),
                                             QMessageBox::Ok|QMessageBox::Cancel) ==
                                                 QMessageBox::Ok);
        } else {
          switch (p_type)
          {
            case POPUP_RECVD_MESSAGE: {
              m_messageBox.setText(QString::fromUtf8(
                  "Tiens! Un nouveau message est arrivé!\n"
                  "OK pour recevoir la chicorée?"));
              QPixmap _pixmap(":/images/popup_chat.png");
              m_messageBox.setIconPixmap(_pixmap);
              _show = (m_messageBox.exec() == QMessageBox::Ok);
              break;
            }
            case POPUP_RECVD_CANVASS: {
              m_messageBox.setText(QString::fromUtf8(
                  "Tiens! Un nouveau sondage a été lancé!\n"
                  "OK pour recevoir la chicorée?"));
              QPixmap _pixmap(":/images/popup_canvass.png");
              m_messageBox.setIconPixmap(_pixmap);
              _show = (m_messageBox.exec() == QMessageBox::Ok);
              break;
            }
            case POPUP_RECVD_CANVASS_VOTE: {
              m_messageBox.setText(QString::fromUtf8(
                  "Tiens! Un nouveau vote a été collecté!\n"
                  "Tu veux jeter un coup d'oeil?"));
              QPixmap _pixmap(":/images/popup_canvass.png");
              m_messageBox.setIconPixmap(_pixmap);
              _show = (m_messageBox.exec() == QMessageBox::Ok);
              break;
            }
            case POPUP_RECVD_FUCK_NOTIF:
            case POPUP_RECVD_SUCK_NOTIF:
            case POPUP_RECVD_LIKE_NOTIF:
            case POPUP_RECVD_POETRY_NOTIF: {
              m_messageBox.setText(QString::fromUtf8(
                  "Tiens! Quelqu'un a réagi à un de tes messages!\n"
                  "T'es sûr de vouloir savoir?"));
              QPixmap _pixmap(":/images/popup_wakeup.png");
              m_messageBox.setIconPixmap(_pixmap);
              _show = (m_messageBox.exec() == QMessageBox::Ok);
              break;
            }
            case POPUP_RECVD_OOPS:
            default:
              _show = false;
              break;

          }
        }

        m_promptMutex.lock();
        _promptInProgress = false;
        m_promptMutex.unlock();
      }
    }
  }

  return _show;
}

void PopupMessageWidget::slot_showMessage(const UserMessage *p_message,
                                                PopupThreadWidget *p_widget)
{
  PopupUserExt _me;
  getUserInfo(_me);
  bool _show = displayAuthorized(POPUP_RECVD_MESSAGE);

  if (_show)
  {
    bool _showMedia = (p_message->senderID != _me.getID() &&
                          ((_me.getMode() == POPUP_MODE_DEFAULT)  ||
                           (_me.getMode() == POPUP_MODE_PROMPT)||
                           (QObject::sender() == this)));
    if (_showMedia)
    {
      // Iterate on each media...
      AttachmentList::const_iterator _it;
      for (_it = p_message->files.begin(); _it != p_message->files.end(); _it++)
      {
        //---
        // Image
        if (isSupportedImage(*_it)) {
          if (PopupSettings::instance()->automaticImageViewerShow())
          {
            if (PopupSettings::instance()->useEmbeddedImageViewer())
            {
              slot_showImage(_it->c_str());
            }
            else
            {
              openImage(*_it);
            }
          }
        }
        // Other media
        else {
          openMedia(*_it);
        }
      }
      // Iterate on each URL...
      for (_it = p_message->URLs.begin(); _it != p_message->URLs.end(); _it++)
      {
        openUrl(*_it);
      }
    }
  }

  // We highlight the concerned widget is any case
  highlightWidget(p_widget, _show);
}
//----------------------------------------------------------

void PopupMessageWidget::highlightWidget(QWidget *p_widget, bool p_show)
{
  // Current tab is not the one we've updated
  if (currentWidget() != p_widget)
  {
    // Set a warning icon instead of default one in case
    // message dialog is already visible and has the focus <=> user is using it!
    // or
    // message dialog is not visible but user want doesn't want to let it shown
    if (m_parent->isVisible() ||
        (!m_parent->isVisible() && !p_show))
    {
      int _index = indexOf(p_widget);
      QIcon _icon;
      _icon.addFile(QString::fromUtf8(":/images/popup_wakeup.png"),
                    QSize(), QIcon::Normal, QIcon::Off);
      setTabIcon(_index, _icon);
      tabBar()->setTabTextColor(_index,Qt::red);
      m_highlightedTabs.insert(_index);
    }
    else
    {
      setCurrentWidget(p_widget);
    }
  }

  // Show windows if we're allowed.
  if (p_show && !m_parent->isVisible()) {
      m_parent->setVisible(true);
  }
}

//----------------------------------------------------------

void PopupMessageWidget::slot_showImage(const QString & p_image,
                                        const QString & p_sender)
{
  emit signal_showImage(p_image, p_sender);
}

//=============================================================================
// In messages management
//=============================================================================

void PopupMessageWidget::slot_messageReceived(UserMessage p_message)
{
  processNewMessage(p_message);
}
//----------------------------------------------------------

void PopupMessageWidget::slot_messageReactionReceived(
    RateUserMessage p_reaction)
{
  m_mutex.lock();

  // Search for an existing thread matching this message's one
  map<ThreadID, PopupThreadWidget*>::iterator _it =
      m_threads.find(p_reaction.threadID);

  // Found! Let's point on it
  if (_it != m_threads.end())
  {
    _it->second->processMessageReaction(p_reaction);
  }

  m_mutex.unlock();

  if ((p_reaction.msgSenderID == getMyID()) &&
      (displayAuthorized(convertRateItemToRecvdMsgType(p_reaction.rateItem))))
  {
    QString _senderName = getNickname(p_reaction.rateSenderID).c_str();

    switch (p_reaction.rateItem) {
        case POPUP_RATE_FUCKYOU:
          emit signal_showImage(":/images/popup_bigfuckyou.png",
                                _senderName, true);
          break;
        case POPUP_RATE_DONTLIKE:
          emit signal_showImage(":/images/popup_bigdontlike.png",
                                _senderName, true);
          break;
        case POPUP_RATE_LIKE:
          emit signal_showImage(":/images/popup_biglike.png",
                                _senderName, true);
          break;
        case POPUP_RATE_POETRY:
          emit signal_showImage(":/images/popup_bigpointfinesse.png",
                                _senderName, true);
          break;
        default:
          break;
    }
  }
}
//----------------------------------------------------------

void PopupMessageWidget::slot_onOpinionRequested(Popup::Canvass p_canvass)
{
  int _newTabIndex = count()-1;

  PopupCanvassWidget *_widget = new PopupCanvassWidget(this, p_canvass);

  connect(_widget, SIGNAL(signal_sendVote(Popup::Vote*)),
          this, SLOT(slot_sendVote(Popup::Vote*)));

  m_opinionRequests[p_canvass.canvassID] = _widget;

  QIcon _icon;
  _icon.addFile(QString::fromUtf8(":/images/popup_canvass.png"),
                QSize(), QIcon::Normal, QIcon::Off);
  insertTab(_newTabIndex, _widget, _icon, "Opinion request");

  QIcon _icon2;
  _icon2.addFile(QString::fromUtf8(":/images/popup_close_tab.png"),
                 QSize(), QIcon::Normal, QIcon::Off);
  QPushButton *_closeBtn = new QPushButton(_icon2, "", this);
  _closeBtn->setMaximumSize(15,15);
  tabBar()->setTabButton(_newTabIndex, QTabBar::RightSide, _closeBtn);
  connect(_closeBtn, SIGNAL(clicked()),
          this, SLOT(slot_closeCurrentThread()));

  highlightWidget(_widget, displayAuthorized(POPUP_RECVD_CANVASS));
}
//----------------------------------------------------------

void PopupMessageWidget::slot_onVoteReceived(Vote p_vote)
{
  std::map<Popup::CanvassID, PopupCanvassWidget*>::iterator _it =
      m_opinionRequests.find(p_vote.canvassID);
  if (_it != m_opinionRequests.end()) {
    PopupCanvassWidget *_widget = _it->second;
    _widget->processVote(p_vote);
    highlightWidget(_widget, displayAuthorized(POPUP_RECVD_CANVASS_VOTE));
  }
}
//----------------------------------------------------------

void PopupMessageWidget::slot_onInvitationReceived(ThreadInvitation p_invitation)
{
  PopupThreadWidget *_twidget = 0;
  map<ThreadID, PopupThreadWidget*>::iterator _it;
  bool _isCc = false;

  // Invitation concerns myself!
  if (p_invitation.invitedUsers.find(getMyID()) !=
      p_invitation.invitedUsers.end())
  {
    m_mutex.lock();

    // We should not find any existing corresponding thread!
    _it = m_threads.find(p_invitation.threadID);
    // If so, create new thread (Otherwise, message is ignored)
    if (_it == m_threads.end()) {
      _twidget = createNewThread(p_invitation.threadID,
               p_invitation.threadTitle.c_str());
    }
    m_mutex.unlock();
  }

  // Invitation concerns another user. We are only copied
  else
  {
    _isCc = true;

    m_mutex.lock();
    // Search for existing thread matching this message's one
    _it = m_threads.find(p_invitation.threadID);
    if (_it != m_threads.end()) {
      _twidget = _it->second;
    }
    m_mutex.unlock();
  }

  if (_twidget != 0)
  {
    _twidget->processInvitation(p_invitation, _isCc);
  }
}

//=============================================================================
// Out messages management (simple pass-through)
//=============================================================================


void PopupMessageWidget::slot_messageSent(UserMessage p_message)
{
  processNewMessage(p_message);
}
//----------------------------------------------------------

void PopupMessageWidget::slot_sendMessage(UserMessage *p_message)
{
  emit signal_sendMessage(p_message);
}
//----------------------------------------------------------

void PopupMessageWidget::slot_sendMessageReaction(RateUserMessage *p_reaction)
{
  emit signal_sendMessageReaction(p_reaction);
}
//----------------------------------------------------------

void PopupMessageWidget::slot_sendVote(Vote *p_vote)
{
  emit signal_sendVote(p_vote);
}
//----------------------------------------------------------

void PopupMessageWidget::slot_sendInvitation(ThreadInvitation *p_invitation)
{
  emit signal_sendInvitation(p_invitation);
}

//=============================================================================
// Tabs management
//=============================================================================

void PopupMessageWidget::slot_closeCurrentThread()
{
  PopupThreadWidget *_twidget = 0;

  if (confirm("Are you sure you want to delete this thread?", this)) {
    m_mutex.lock();

    bool _found = false;
    QObject *_sender = QObject::sender();
    int _index = 0;

    while ((_found == false) && (_index < count())) {
      if (_sender ==
          (QObject*) tabBar()->tabButton(_index, QTabBar::RightSide)) {
        _found = true;
      }
      else
      {
        _index++;
      }
    }

    if (_found) {
      _twidget = static_cast<PopupThreadWidget*>(widget(_index));
      removeTab(_index);
      m_highlightedTabs.erase(_index);
      std::map<Popup::ThreadID, PopupThreadWidget*>::iterator _it;
      for (_it = m_threads.begin(); _it != m_threads.end(); _it++) {
        if (_it->second == _twidget) {
          m_threads.erase(_it);
          break;
        }
      }
    }
    m_mutex.unlock();

    if (_twidget != 0) {
      delete _twidget;
    }
  }
}
//---------------------------------------------------------

void PopupMessageWidget::slot_currentChanged(int p_index)
{
  if (m_highlightedTabs.find(p_index) != m_highlightedTabs.end()) {
    m_highlightedTabs.erase(p_index);
    QIcon _icon;
    if (dynamic_cast<PopupThreadWidget*>(widget(p_index)) != 0) {
      _icon.addFile(QString::fromUtf8(":/images/popup_message.png"),
                    QSize(), QIcon::Normal, QIcon::Off);
    } else if (dynamic_cast<PopupCanvassWidget*>(widget(p_index)) != 0) {
      _icon.addFile(QString::fromUtf8(":/images/popup_canvass.png"),
                    QSize(), QIcon::Normal, QIcon::Off);
    }
    setTabIcon(p_index, _icon);
    tabBar()->setTabTextColor(p_index, tabBar()->tabTextColor(count()-1));
  }
}
//----------------------------------------------------------

void PopupMessageWidget::slot_applyNewSettings()
{
  emit(signal_applyNewSettings());
}
//----------------------------------------------------------

PopupThreadWidget *PopupMessageWidget::createNewThread(const ThreadID & p_threadID,
                                                       const QString & p_threadTitle,
                                                       const UserList & p_targets,
                                                       bool p_isThreadTitleValid,
                                                       const QString & p_text)
{
  int _newTabIndex = 0;
  // By default, thread title corresponds to the one we've received
  QString _title = p_threadTitle;
  bool _isThreadTitleValid = p_isThreadTitleValid;

  // If thread title is empty, create a default one
  // In this case, we also set the value of '_isThreadTitleValid' to
  // false to tell child widget never to send it to other users
  if (_title.length() == 0)
  {
    _isThreadTitleValid = false;
    _title = QString("[Thread ") + QString::number(anonymousThreadCounter++) + "]";
  }

  m_mutex.lock(); // This will lock only if user is deleting a tab

  // Create new widget
  PopupThreadWidget *_twidget =
      new PopupThreadWidget(this, p_threadID, p_targets,
                            p_threadTitle, _isThreadTitleValid,
                            p_text);

  // Connect it
  connect(_twidget, SIGNAL(signal_sendMessage(Popup::UserMessage*)),
          this, SLOT(slot_sendMessage(Popup::UserMessage*)));
  connect(_twidget, SIGNAL(signal_sendMessageReaction(Popup::RateUserMessage*)),
          this, SLOT(slot_sendMessageReaction(Popup::RateUserMessage*)));
  connect(_twidget, SIGNAL(signal_sendInvitation(Popup::ThreadInvitation*)),
          this, SLOT(slot_sendInvitation(Popup::ThreadInvitation*)));
  connect(_twidget, SIGNAL(signal_showImage(const QString &, const QString &)),
          this, SLOT(slot_showImage(const QString &, const QString &)));
  connect(this, SIGNAL(signal_applyNewSettings()),
          _twidget, SLOT(slot_applyNewSettings()));

  // Store its reference into our map
  m_threads[p_threadID] = _twidget;

  m_mutex.unlock();

  _newTabIndex = count()-1;
  QIcon _icon1;
  _icon1.addFile(QString::fromUtf8(":/images/popup_message.png"),
                 QSize(), QIcon::Normal, QIcon::Off);
  insertTab(_newTabIndex, _twidget, _icon1, _title);
  QIcon _icon2;
  _icon2.addFile(QString::fromUtf8(":/images/popup_close_tab.png"),
               QSize(), QIcon::Normal, QIcon::Off);
  QPushButton *_closeBtn = new QPushButton(_icon2, "", this);
  _closeBtn->setMaximumSize(15,15);
  tabBar()->setTabButton(_newTabIndex, QTabBar::RightSide, _closeBtn);
  connect(_closeBtn, SIGNAL(clicked()),
          this, SLOT(slot_closeCurrentThread()));

  return _twidget;
}
//----------------------------------------------------------

void PopupMessageWidget::slot_onStart()
{
  if (m_threadStartWidget->validate())
  {
    switch (m_threadStartWidget->getMode())
    {
      case PopupThreadStartWidget::MODE_DEFAULT:
        startThread();
        break;
      case PopupThreadStartWidget::MODE_CANVASS:
        startCanvass();
        break;
    }
  }
}
//----------------------------------------------------------

void PopupMessageWidget::startThread()
{
  ThreadID _newThreadID = ((ThreadID) getSessionID() << 32) | threadCounter;
  threadCounter++;
  UserList _targets;
  QString _title = "";
  m_threadStartWidget->getNewThreadInfo(_title, _targets);
  PopupThreadWidget *_threadWidget =
      createNewThread(_newThreadID, _title, _targets);
  setCurrentWidget(_threadWidget);
}
//----------------------------------------------------------

void PopupMessageWidget::startCanvass()
{
  Canvass _canvass;
  _canvass.canvassID = ((CanvassID) getSessionID() << 32) | threadCounter;
  threadCounter++;
  m_threadStartWidget->getNewCanvassInfo(_canvass);
  emit signal_submitCanvass(&_canvass);
}
//----------------------------------------------------------

void PopupMessageWidget::getThreadWidgets(vector<const PopupThreadWidget*> & p_widgets)
{
  m_mutex.lock();
  collection_foreach(m_threads, PopupThreadWidgetMap, _it)
  {
    p_widgets.push_back(_it->second);
  }
  m_mutex.unlock();
}
//----------------------------------------------------------

void PopupMessageWidget::addThread(const PopupGui::PopupThreadSave & p_saved)
{
  PopupThreadWidget *_threadWidget =
      createNewThread(p_saved.ID,
                      p_saved.title,
                      p_saved.users,
                      false,
                      p_saved.text);
  if (p_saved.isActive) {
    setCurrentWidget(_threadWidget);
  }
}
